﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Bankeren.Engine.Cards;
using System.Diagnostics;
using Bankeren.Service;
using System.Security.Cryptography;
using System.Windows.Media;

namespace Bankeren.Engine
{
    public class Game
    {
        #region Fields

        internal const int MAXPLAYERS = 5;

        private Server server;
        private Random random;
        private Dictionary<int, Player> players;
        private Dictionary<int, int> points;
        private Thread gameThread;
        private Round currentRound;

        #endregion

        #region Properties

        public Dictionary<int, Player> Players
        {
            get
            {
                return this.players;
            }
        }

        public Dictionary<int, PlayerInfo> PlayerInfos
        {
            get
            {
                lock (players)
                {
                    Dictionary<int, PlayerInfo> infos = new Dictionary<int, PlayerInfo>();
                    foreach (int seat in players.Keys)
                    {
                        infos.Add(seat, players[seat].Info);
                    }
                    return infos;
                }
            }
        }

        #endregion

        #region Ctor

        public Game(Server server)
        {
            this.server = server;
            this.random = new Random();
            this.players = new Dictionary<int, Player>();
            this.points = new Dictionary<int, int>();
            this.gameThread = new Thread(PlayGame);
            this.gameThread.Start();
        }

        #endregion

        #region Methods

        #region Public Methods

        public Human AddHuman(IBankerenCallback callback, PlayerInfo info)
        {
            if (this.players.Count < MAXPLAYERS)
            {
                int seat = this.players.Count;
                Human human = new Human(callback, this, info, seat);
                // Notify already connected players of new player
                foreach (Player player in players.Values)
                    player.PlayerJoined(human.Seat, info);
                this.players.Add(seat, human);
                // Send welcome message to new player
                human.Welcome(human.Seat, PlayerInfos);
                Log("Human \"{0}\" joined", human.Info.Name);
                return human;
            }
            return null;
        }

        public void AddBot(string name, Bot.Difficulty diff)
        {
            if (this.players.Count < MAXPLAYERS)
            {
                int seat = this.players.Count;
                RandomNumberGenerator rndGen = System.Security.Cryptography.RandomNumberGenerator.Create();
                byte[] b = new byte[3];
                rndGen.GetBytes(b);
                Color color = Color.FromArgb(255, b[0], b[1], b[2]);
                PlayerInfo info = new PlayerInfo(name, color.ToString(), new Avatar("robot"));
                // Notify already connected players of new player
                foreach (Player player in players.Values)
                    player.PlayerJoined(seat, info);
                Bot bot = new Bot(diff, this, info, seat);
                // Send welcome message to new player
                bot.Welcome(bot.Seat, PlayerInfos);
                this.players.Add(seat, bot);
                Log("Bot \"{0}\" added", name);
            }
        }

        public void MakeBid(int seat, int bid)
        {
            currentRound.PlaceBid(seat, bid);
        }

        public bool PlayCard(Player player, Card card)
        {
            return currentRound.PlayCard(player, card);
        }

        public void Chat(int seat, string message)
        {
            foreach (Player player in players.Values)
                player.Chat(seat, message);
        }

        public void Start()
        {
            this.gameThread.Start();
        }

        #endregion

        #region Private Methods

        private void PlayGame()
        {
            // Wait for five players...
            Log("Waiting for players...");
            while (players.Count < MAXPLAYERS)
                Thread.Sleep(1000);
            Log("Starting");
            int roundNumber = 1;
            int dealer = random.Next(MAXPLAYERS);
            while (roundNumber <= 20)
            {
                // Start new round
                currentRound = new Round(this, dealer, roundNumber);
                Dictionary<int, int> roundPoints = currentRound.PlayRound();
                foreach (int seat in roundPoints.Keys)
                {
                    if (points.ContainsKey(seat))
                        points[seat] += roundPoints[seat];
                    else
                        points.Add(seat, roundPoints[seat]);
                }
                // Next dealer
                dealer = ++dealer % MAXPLAYERS;
                roundNumber++;
            }
            Log("Points:");
            foreach (int seat in points.Keys)
                Log(players[seat] + ": " + points[seat]);
            Log("Finished");
        }

        #endregion

        #region Internal Methods

        internal void Log(string format, params object[] args)
        {
            server.WriteToConsole("Game: " + format, args);
        }

        #endregion

        #endregion
    }
}
